﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.SqlServer.Dts.Pipeline;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

namespace ToddMcDermid.SSIS
{
    /// <summary>
    /// 
    /// </summary>
    [DtsPipelineComponent(
        DisplayName = "HTML Table Destination",
        CurrentVersion = 3,
        ComponentType = ComponentType.DestinationAdapter,
        UITypeName = "ToddMcDermid.SSIS.HTMLTableDestinationInterface,HTMLTableDestination100,Version=1.0.0.0,Culture=neutral,PublicKeyToken=c263bd6451c2434e",
        IconResource = "ToddMcDermid.SSIS.HTMLTableDestination.ico"
        )]
    public class HTMLTableDestination : PipelineComponent
    {
        #region Constants
        private const string PROPERTY_NAME_OUTPUT_VARIABLE = "Output Variable";
        private const string COLUMN_NAME_ROW_COLOUR = "Row Colour Column Name";
        private const string INPUT_NAME = "Input";
        private const string PROPERTY_NAME_REPLACE_NEWLINE_WITH_BREAK = "Replace Newline With Break";
        private const string PROPERTY_NAME_ROW_COUNT_VARIABLE = "Row Count Variable";
        private const string PROPERTY_NAME_COLUMN_ORDER = "Column Order";
        #endregion

        #region Private Variables Used at Runtime
        private string _outputTableVariableName;
        private string _rowColourColumn;
        private string _rowCountVariableName;
        private bool _replaceNewlineWithBreak;

        private int _rowColourColumnBufferIndex;
        private StringBuilder _outputTable;

        struct RuntimeColumnDefinition
        {
            public string Name;
            public DataType Type;
            public int BufferIndex;
        }

        private List<RuntimeColumnDefinition> _columns;
        private int _totalRowsProcessed;
        #endregion

        #region Constructor
        /// <summary>
        /// </summary>
        public HTMLTableDestination()
        { }
        #endregion

        #region ProvideComponentProperties
        /// <summary>
        /// Initializes the component with default settings
        /// </summary>
        /// <remarks>Called by SSIS when the component is first added to a dataflow task</remarks>
        public override void ProvideComponentProperties()
        {
            this.RemoveAllInputsOutputsAndCustomProperties();
            this.ComponentMetaData.UsesDispositions = true;

            this.AddOutputVariableProperty();
            this.AddRowColourColumnProperty();
            this.AddReplaceNewlineWithBreakProperty();
            this.AddRowCountVariableProperty();
            this.AddColumnOrderProperty();

            IDTSInput100 input = this.ComponentMetaData.InputCollection.New();
            input.Name = INPUT_NAME;
            input.HasSideEffects = true;

            #region Set metadata version
            DtsPipelineComponentAttribute componentAttribute =
              (DtsPipelineComponentAttribute)Attribute.GetCustomAttribute(this.GetType(), typeof(DtsPipelineComponentAttribute), false);
            int runtimeVersion = componentAttribute.CurrentVersion;
            ComponentMetaData.Version = runtimeVersion;
            #endregion
        }
        #endregion

        #region PerformUpgrade
        public override void PerformUpgrade(int pipelineVersion)
        {
            // Obtain the current component version from the attribute.
            DtsPipelineComponentAttribute componentAttribute =
              (DtsPipelineComponentAttribute)Attribute.GetCustomAttribute(this.GetType(), typeof(DtsPipelineComponentAttribute), false);
            int runtimeVersion = componentAttribute.CurrentVersion;
            int metadataVersion = ComponentMetaData.Version;

            if (runtimeVersion != metadataVersion)
            {
                #region Upgrade to v1
                if (metadataVersion <= 0)
                {
                    ComponentMetaData.InputCollection[0].HasSideEffects = true;
                    if (HTMLTableDestination.GetOutputVariableProperty(ComponentMetaData) == null)
                    {
                        this.AddOutputVariableProperty();
                    }
                    if (HTMLTableDestination.GetRowColourColumnProperty(ComponentMetaData) == null)
                    {
                        this.AddRowColourColumnProperty();
                    }
                    if (HTMLTableDestination.GetReplaceNewlineWithBreakProperty(ComponentMetaData) == null)
                    {
                        this.AddReplaceNewlineWithBreakProperty();
                    }
                }
                #endregion

                #region Upgrade to v2
                if (metadataVersion <= 1)
                {
                    this.AddRowCountVariableProperty();
                }
                #endregion

                #region Upgrade to v3
                if (metadataVersion <= 2)
                {
                    int[] columnOrder = new int[ComponentMetaData.InputCollection[0].InputColumnCollection.Count];
                    for (int index = 0; index < ComponentMetaData.InputCollection[0].InputColumnCollection.Count; index++)
                    {
                        columnOrder[index] = ComponentMetaData.InputCollection[0].InputColumnCollection[index].LineageID;
                    }

                    this.AddColumnOrderProperty();
                    IDTSCustomProperty100 columnOrderProperty = HTMLTableDestination.GetColumnOrderProperty(ComponentMetaData);
                    columnOrderProperty.Value = columnOrder;
                }
                #endregion

                // Update the saved component version metadata to the current version.
                ComponentMetaData.Version = runtimeVersion;
            }
        }
        #endregion

        #region Validate
        /// <summary>
        /// Verifies that the component is correctly configured
        /// </summary>
        /// <remarks>Called repeatedly by SSIS during design time for validation and once before execution</remarks>
        /// <returns>The status of the validation</returns>
        public override DTSValidationStatus Validate()
        {
            DTSValidationStatus status = base.Validate();

            if (status == DTSValidationStatus.VS_ISVALID)
            {
                bool pbCancel;

                #region Check Output Table Variable
                IDTSCustomProperty100 outputVariableProperty = HTMLTableDestination.GetOutputVariableProperty(ComponentMetaData);
                if (outputVariableProperty == null)
                {
                    status = DTSValidationStatus.VS_ISCORRUPT;
                    ComponentMetaData.FireError(0, ComponentMetaData.Name, "Output Table Variable property does not exist.", "", 0, out pbCancel);
                }
                else if ((string)outputVariableProperty.Value == "")
                {
                    status = DTSValidationStatus.VS_ISBROKEN;
                    ComponentMetaData.FireError(0, ComponentMetaData.Name, "Output Table Variable not specified.", "", 0, out pbCancel);
                }
                else if (!VariableDispenser.Contains((string)outputVariableProperty.Value))
                {
                    status = DTSValidationStatus.VS_ISBROKEN;
                    ComponentMetaData.FireError(0, ComponentMetaData.Name, "Output Table Variable '" + (string)outputVariableProperty.Value + "' can not be found.", "", 0, out pbCancel);
                }
                else
                {
                    IDTSVariables100 vars = null;
                    VariableDispenser.LockForRead((string)outputVariableProperty.Value);
                    VariableDispenser.GetVariables(out vars);
                    Variables varList = DtsConvert.GetWrapper(vars); 
                    if (varList[0].DataType != TypeCode.String)
                    {
                        status = DTSValidationStatus.VS_ISBROKEN;
                        ComponentMetaData.FireError(0, ComponentMetaData.Name, "Output Table Variable '" + (string)outputVariableProperty.Value + "' isn't a String type.", "", 0, out pbCancel);
                    }
                    vars.Unlock();
                }
                #endregion

                #region Check Row Column Column
                IDTSCustomProperty100 rowColourColumnProperty = HTMLTableDestination.GetRowColourColumnProperty(ComponentMetaData);
                if (rowColourColumnProperty != null)
                {
                    if ((string)rowColourColumnProperty.Value != "")
                    {
                        IDTSInput100 input = this.GetInput();
                        if ((input != null) && (input.IsAttached))
                        {
                            IDTSVirtualInput100 virtualInput = input.GetVirtualInput();
                            IDTSVirtualInputColumn100 rowColourColumn = null;
                            foreach (IDTSVirtualInputColumn100 vColumn in virtualInput.VirtualInputColumnCollection)
                            {
                                if (vColumn.Name == (string)rowColourColumnProperty.Value)
                                {
                                    rowColourColumn = vColumn;
                                }
                            }

                            if (rowColourColumn == null)
                            {
                                status = DTSValidationStatus.VS_ISBROKEN;
                                ComponentMetaData.FireError(0, ComponentMetaData.Name, "Row colour column '" + (string)rowColourColumnProperty.Value + "' can not be found.", "", 0, out pbCancel);
                            }
                            else if ((rowColourColumn.DataType != DataType.DT_WSTR)
                                && (rowColourColumn.DataType != DataType.DT_STR))
                            {
                                status = DTSValidationStatus.VS_ISBROKEN;
                                ComponentMetaData.FireError(0, ComponentMetaData.Name, "Row colour column '" + (string)rowColourColumnProperty.Value + "' isn't a DT_WSTR or DT_STR type.", "", 0, out pbCancel);
                            }
                            else if (rowColourColumn.UsageType != DTSUsageType.UT_READONLY)
                            {
                                status = DTSValidationStatus.VS_NEEDSNEWMETADATA;
                                ComponentMetaData.FireError(0, ComponentMetaData.Name, "Row colour column '" + (string)rowColourColumnProperty.Value + "' "
                                    + "must be marked READONLY as an input column.", "", 0, out pbCancel);
                            }
                        }
                    }
                }
                #endregion

                #region Check Newline Conversion Setting
                IDTSCustomProperty100 replaceNewlineProperty = HTMLTableDestination.GetReplaceNewlineWithBreakProperty(ComponentMetaData);
                if (replaceNewlineProperty == null)
                {
                    status = DTSValidationStatus.VS_ISCORRUPT;
                    ComponentMetaData.FireError(0, ComponentMetaData.Name, "Replace newline with break property does not exist.", "", 0, out pbCancel);
                }
                #endregion

                #region Check Row Count Variable
                IDTSCustomProperty100 rowCountVariableProperty = HTMLTableDestination.GetRowCountVariableProperty(ComponentMetaData);
                if (rowCountVariableProperty == null)
                {
                    status = DTSValidationStatus.VS_ISCORRUPT;
                    ComponentMetaData.FireError(0, ComponentMetaData.Name, "Row count variable property does not exist.", "", 0, out pbCancel);
                }
                else if ((string)rowCountVariableProperty.Value != "")
                {
                    if (!VariableDispenser.Contains((string)rowCountVariableProperty.Value))
                    {
                        status = DTSValidationStatus.VS_ISBROKEN;
                        ComponentMetaData.FireError(0, ComponentMetaData.Name, "Row count variable '" + (string)rowCountVariableProperty.Value + "' can not be found.", "", 0, out pbCancel);
                    }
                }
                #endregion

                #region Check Column Order
                IDTSCustomProperty100 columnOrderProperty = HTMLTableDestination.GetColumnOrderProperty(ComponentMetaData);
                if (columnOrderProperty == null)
                {
                    status = DTSValidationStatus.VS_ISCORRUPT;
                    ComponentMetaData.FireError(0, ComponentMetaData.Name, "Column order property does not exist.", "", 0, out pbCancel);
                }
                else
                {
                    int[] columnOrder = (int[])columnOrderProperty.Value;

                    if (columnOrder.Length <= 0)
                    {
                        status = DTSValidationStatus.VS_ISBROKEN;
                        ComponentMetaData.FireError(0, ComponentMetaData.Name, "No columns have been included in the output table.", "", 0, out pbCancel);
                    }
                    else
                    {
                        IDTSInput100 input = this.GetInput();
                        IDTSVirtualInput100 vInput = input.GetVirtualInput();
                        for (int index = 0; index < columnOrder.Length; index++)
                        {
                            bool foundColumn = false;
                            foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
                            {
                                if (vColumn.LineageID == columnOrder[index])
                                {
                                    foundColumn = true;
                                    if (vColumn.UsageType != DTSUsageType.UT_READONLY)
                                    {
                                        status = DTSValidationStatus.VS_NEEDSNEWMETADATA;
                                        ComponentMetaData.FireError(0, ComponentMetaData.Name, "Column '" + vColumn.Name + "' (" + vColumn.LineageID.ToString() + ") "
                                            + "usage is set to " + vColumn.UsageType.ToString() + ", and must be set to " + DTSUsageType.UT_READONLY.ToString() + ".", "", 0, out pbCancel);
                                    }
                                    break;
                                }
                            }
                            if (!foundColumn)
                            {
                                status = DTSValidationStatus.VS_NEEDSNEWMETADATA;
                                ComponentMetaData.FireError(0, ComponentMetaData.Name, "Column specified in output table does not exist in input data flow.", "", 0, out pbCancel);
                            }
                        }
                    }
                }
                #endregion
            }

            return status;
        }
        #endregion

        #region ReinitializeMetaData
        /// <summary>
        /// </summary>
        public override void ReinitializeMetaData()
        {
            HTMLTableDestination.ReinitializeMetaData(ComponentMetaData);
        }

        public static void ReinitializeMetaData(IDTSComponentMetaData100 componentMetaData)
        {
            IDTSInput100 input = componentMetaData.InputCollection[0];
            IDTSVirtualInput100 vInput = input.GetVirtualInput();

            #region Standard
            if (!componentMetaData.AreInputColumnsValid)
            {
                foreach (IDTSInputColumn100 column in input.InputColumnCollection)
                {
                    IDTSVirtualInputColumn100 vColumn = vInput.VirtualInputColumnCollection.GetVirtualInputColumnByLineageID(column.LineageID);

                    if (vColumn == null)
                    {
                        input.InputColumnCollection.RemoveObjectByID(column.ID);
                    }
                }
            }
            #endregion

            string rowColourColumnName = null;
            #region Ensure Row Colour Column is set to ReadOnly
            IDTSCustomProperty100 rowColourColumnProperty = HTMLTableDestination.GetRowColourColumnProperty(componentMetaData);
            if (rowColourColumnProperty != null)
            {
                if ((string)rowColourColumnProperty.Value != "")
                {
                    rowColourColumnName = (string)rowColourColumnProperty.Value;
                    foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
                    {
                        if (vColumn.Name == rowColourColumnName)
                        {
                            vInput.SetUsageType(vColumn.LineageID, DTSUsageType.UT_READONLY);
                        }
                    }
                }
            }
            #endregion

            #region Fix Column Order and Usage
            IDTSCustomProperty100 columnOrderProperty = HTMLTableDestination.GetColumnOrderProperty(componentMetaData);
            int[] columnOrder = (int[])columnOrderProperty.Value;
            #region Get List of Columns Found (and fix their usage)
            List<int> columnsFound = new List<int>();
            for (int index = 0; index < columnOrder.Length; index++)
            {
                foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
                {
                    if (vColumn.LineageID == columnOrder[index])
                    {
                        columnsFound.Add(vColumn.LineageID);
                        if (vColumn.UsageType != DTSUsageType.UT_READONLY)
                        {
                            vInput.SetUsageType(vColumn.LineageID, DTSUsageType.UT_READONLY);
                        }
                        break;
                    }
                }
            }
            #endregion
            #region Mark Usage of all other columns (except colour) as unused
            foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
            {
                if (!columnsFound.Contains(vColumn.LineageID))
                {
                    if (vColumn.UsageType == DTSUsageType.UT_READONLY)
                    {
                        if ((rowColourColumnName != null) && (vColumn.Name != rowColourColumnName))
                        {
                            vInput.SetUsageType(vColumn.LineageID, DTSUsageType.UT_IGNORED);
                        }
                    }
                }
            }
            #endregion
            #region Fix up column order
            int[] newColumnOrder = new int[columnsFound.Count];
            int newColumnIndex = 0;
            for (int index = 0; index < columnOrder.Length; index++)
            {
                if (columnsFound.Contains(columnOrder[index]))
                {
                    newColumnOrder[newColumnIndex] = columnOrder[index];
                    newColumnIndex++;
                }
            }
            columnOrderProperty.Value = newColumnOrder;
            #endregion
            #endregion
        }
        #endregion

        #region SetUsageType
        public override IDTSInputColumn100 SetUsageType(int inputID, IDTSVirtualInput100 virtualInput, int lineageID, DTSUsageType usageType)
        {
            // Disallow read write columns.
            if (usageType == DTSUsageType.UT_READWRITE)
            {
                throw new Exception("Columns must be UT_READONLY");
            }

            return base.SetUsageType(inputID, virtualInput, lineageID, usageType);
        }
        #endregion

        #region OnInputPathAttached
        /// <summary>
        /// Called when the component is attached to an upstream component. Automatically map all of the columns
        /// from the virtual input by calling SetUsageType.
        /// </summary>
        /// <param name="inputID">The ID of the input that is attached.</param>
        public override void OnInputPathAttached(int inputID)
        {
            IDTSInput100 input = ComponentMetaData.InputCollection.GetObjectByID(inputID);

            IDTSVirtualInput100 vInput = input.GetVirtualInput();

            foreach (IDTSVirtualInputColumn100 vCol in vInput.VirtualInputColumnCollection)
            {
                this.SetUsageType(inputID, vInput, vCol.LineageID, DTSUsageType.UT_READONLY);
            }
        }
        #endregion

        #region PreExecute
        /// <summary>
        /// Set up the dataset 
        /// For each IDTSInput100, create a DataTable in the DataSet
        /// For each IDTSInputColumn100, create a DataColumn in the DataTable.
        /// During ProcessInput these DataTables and DataColumns are populated from the Buffer provided by 
        /// the DataFlow engine.
        /// </summary>
        public override void PreExecute()
        {
            int colCount = 0;
            bool fireAgain = true;

            IDTSCustomProperty100 outputVariableProperty = HTMLTableDestination.GetOutputVariableProperty(ComponentMetaData);
            this._outputTableVariableName = (string)outputVariableProperty.Value;
            IDTSCustomProperty100 rowColourColumnProperty = HTMLTableDestination.GetRowColourColumnProperty(ComponentMetaData);
            this._rowColourColumn = (string)rowColourColumnProperty.Value;
            IDTSCustomProperty100 replaceNewlineProperty = HTMLTableDestination.GetReplaceNewlineWithBreakProperty(ComponentMetaData);
            this._replaceNewlineWithBreak = (bool)replaceNewlineProperty.Value;
            IDTSCustomProperty100 rowCountVariableProperty = HTMLTableDestination.GetRowCountVariableProperty(ComponentMetaData);
            this._rowCountVariableName = (string)rowCountVariableProperty.Value;

            this._columns = new List<RuntimeColumnDefinition>();

            IDTSInput100 input = this.GetInput();
            for (int col = 0; col < input.InputColumnCollection.Count; col++)
            {
                RuntimeColumnDefinition colInfo = new RuntimeColumnDefinition();
                colInfo.Name = input.InputColumnCollection[col].Name;
                colInfo.Type = input.InputColumnCollection[col].DataType;
                colInfo.BufferIndex = BufferManager.FindColumnByLineageID(input.Buffer, input.InputColumnCollection[col].LineageID);
                this._columns.Add(colInfo);

                if (colInfo.Name == this._rowColourColumn)
                {
                    this._rowColourColumnBufferIndex = colInfo.BufferIndex;
                }
            }

            this._totalRowsProcessed = 0;

            this._outputTable = new StringBuilder("<TABLE BORDER=\"1\">");
            this._outputTable.Append("<TR>");
            foreach (RuntimeColumnDefinition colInfo in this._columns)
            {
                if (colInfo.Name != this._rowColourColumn)
                {
                    this._outputTable.Append("<TH>");
                    this._outputTable.Append(colInfo.Name);
                    this._outputTable.Append("</TH>");
                    colCount++;
                }
            }
            this._outputTable.Append("</TR>");

            this.ComponentMetaData.FireProgress("Ready to add rows to table with " + colCount.ToString() + " columns.", 0, 0, 100, this.ComponentMetaData.Name, ref fireAgain);
        }
        #endregion

        #region ProcessInput
        /// <summary>
        /// Add the rows from the input buffer to the to the DataSet.
        /// </summary>
        /// <param name="inputID">The ID of the IDTSInput100</param>
        /// <param name="buffer">The PipelineBuffer containing the records to process</param>
        public override void ProcessInput(int inputID, PipelineBuffer buffer)
        {
            int rowsProcessed = 0;
            bool fireAgain = true;

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }

            while (buffer.NextRow() == true)
            {
                if (this._rowColourColumn == "")
                {
                    this._outputTable.Append("<TR>");
                }
                else
                {
                    this._outputTable.Append("<TR BGCOLOR=\"#" + buffer.GetString(this._rowColourColumnBufferIndex) + "\">");
                }

                foreach (RuntimeColumnDefinition colInfo in this._columns)
                {
                    if (colInfo.Name != this._rowColourColumn)
                    {
                        this._outputTable.Append("<TD>");
                        if (!buffer.IsNull(colInfo.BufferIndex))
                        {
                            switch (colInfo.Type)
                            {
                                case DataType.DT_BOOL:
                                    if (buffer.GetBoolean(colInfo.BufferIndex))
                                    {
                                        this._outputTable.Append("true");
                                    }
                                    else
                                    {
                                        this._outputTable.Append("false");
                                    }
                                    break;

                                case DataType.DT_CY:
                                case DataType.DT_DECIMAL:
                                case DataType.DT_FILETIME:
                                case DataType.DT_NUMERIC:
                                    this._outputTable.Append(buffer.GetDecimal(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_DATE:
                                case DataType.DT_DBDATE:
                                    this._outputTable.Append(buffer.GetDate(colInfo.BufferIndex).ToString("yyyy-MM-dd"));
                                    break;

                                case DataType.DT_DBTIMESTAMP:
                                    this._outputTable.Append(buffer.GetDateTime(colInfo.BufferIndex).ToString("yyyy-MM-dd hh:mm:ss.fff"));
                                    break;

                                case DataType.DT_GUID:
                                    this._outputTable.Append(buffer.GetGuid(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_I1:
                                    this._outputTable.Append(buffer.GetSByte(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_I2:
                                    this._outputTable.Append(buffer.GetInt16(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_I4:
                                    this._outputTable.Append(buffer.GetInt32(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_I8:
                                    this._outputTable.Append(buffer.GetInt64(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_R4:
                                    this._outputTable.Append(buffer.GetSingle(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_R8:
                                    this._outputTable.Append(buffer.GetDouble(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_STR:
                                case DataType.DT_WSTR:
                                    if (this._replaceNewlineWithBreak)
                                    {
                                        this._outputTable.Append(buffer.GetString(colInfo.BufferIndex).Replace("\n", "<br />"));
                                    }
                                    else
                                    {
                                        this._outputTable.Append(buffer.GetString(colInfo.BufferIndex));
                                    }
                                    break;

                                case DataType.DT_UI1:
                                    this._outputTable.Append(buffer.GetByte(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_UI2:
                                    this._outputTable.Append(buffer.GetUInt16(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_UI4:
                                    this._outputTable.Append(buffer.GetUInt32(colInfo.BufferIndex).ToString());
                                    break;

                                case DataType.DT_UI8:
                                    this._outputTable.Append(buffer.GetUInt64(colInfo.BufferIndex).ToString());
                                    break;
                            }
                        }
                        this._outputTable.Append("</TD>");
                    }
                }
                this._outputTable.Append("</TR>");
                rowsProcessed++;
                this._totalRowsProcessed++;
            }

            this.ComponentMetaData.FireProgress(rowsProcessed.ToString() + " rows in buffer processed.", 0, 0, 100, this.ComponentMetaData.Name, ref fireAgain);
        }
        #endregion

        #region PostExecute
        /// <summary>
        /// Called at the end of execution. Saves the dataset as an xml document.
        /// </summary>
        public override void PostExecute()
        {
            bool fireAgain = true;

            this._outputTable.Append("</TABLE>");

            IDTSVariables100 vars = null;
            VariableDispenser.LockForWrite(this._outputTableVariableName);
            if (this._rowCountVariableName != "")
            {
                VariableDispenser.LockForWrite(this._rowCountVariableName);
            }
            VariableDispenser.GetVariables(out vars);
            foreach (IDTSVariable100 variable in vars)
            {
                if ((variable.QualifiedName == this._outputTableVariableName) || (variable.QualifiedName == ("User::" + this._outputTableVariableName)))
                {
                    variable.Value = this._outputTable.ToString();
                }
                else if ((variable.QualifiedName == this._rowCountVariableName) || (variable.QualifiedName == ("User::" + this._rowCountVariableName)))
                {
                    variable.Value = this._totalRowsProcessed;
                }
            }
            vars.Unlock();

            this.ComponentMetaData.FireProgress(this._totalRowsProcessed.ToString() + " total rows processed into " + this._outputTable.Length.ToString() + " characters worth of HTML table.", 100, 0, 100, this.ComponentMetaData.Name, ref fireAgain);
        }
        #endregion

        #region Helpers
        public static IDTSCustomProperty100 GetOutputVariableProperty(IDTSComponentMetaData100 componentMetaData)
        {
            IDTSCustomProperty100 outputVariableProperty = null;

            foreach (IDTSCustomProperty100 customProperty in componentMetaData.CustomPropertyCollection)
            {
                if (customProperty.Name == PROPERTY_NAME_OUTPUT_VARIABLE)
                {
                    outputVariableProperty = customProperty;
                }
            }

            return outputVariableProperty;
        }

        private void AddOutputVariableProperty()
        {
            if (HTMLTableDestination.GetOutputVariableProperty(ComponentMetaData) == null)
            {
                IDTSCustomProperty100 variableProperty = this.ComponentMetaData.CustomPropertyCollection.New();
                variableProperty.Name = PROPERTY_NAME_OUTPUT_VARIABLE;
                variableProperty.Value = "";
                variableProperty.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
            }
        }

        public static IDTSCustomProperty100 GetRowColourColumnProperty(IDTSComponentMetaData100 componentMetaData)
        {
            IDTSCustomProperty100 rowColourColumnProperty = null;

            foreach (IDTSCustomProperty100 customProperty in componentMetaData.CustomPropertyCollection)
            {
                if (customProperty.Name == COLUMN_NAME_ROW_COLOUR)
                {
                    rowColourColumnProperty = customProperty;
                }
            }

            return rowColourColumnProperty;
        }

        private void AddRowColourColumnProperty()
        {
            if (HTMLTableDestination.GetRowColourColumnProperty(ComponentMetaData) == null)
            {
                IDTSCustomProperty100 rowColourProperty = this.ComponentMetaData.CustomPropertyCollection.New();
                rowColourProperty.Name = COLUMN_NAME_ROW_COLOUR;
                rowColourProperty.Value = "";
                rowColourProperty.ExpressionType = DTSCustomPropertyExpressionType.CPET_NONE;
            }
        }

        public static IDTSCustomProperty100 GetReplaceNewlineWithBreakProperty(IDTSComponentMetaData100 componentMetaData)
        {
            IDTSCustomProperty100 replaceNewlineProperty = null;

            foreach (IDTSCustomProperty100 customProperty in componentMetaData.CustomPropertyCollection)
            {
                if (customProperty.Name == PROPERTY_NAME_REPLACE_NEWLINE_WITH_BREAK)
                {
                    replaceNewlineProperty = customProperty;
                }
            }

            return replaceNewlineProperty;
        }

        private void AddReplaceNewlineWithBreakProperty()
        {
            if (HTMLTableDestination.GetReplaceNewlineWithBreakProperty(ComponentMetaData) == null)
            {
                IDTSCustomProperty100 replaceNewlineProperty = this.ComponentMetaData.CustomPropertyCollection.New();
                replaceNewlineProperty.Name = PROPERTY_NAME_REPLACE_NEWLINE_WITH_BREAK;
                replaceNewlineProperty.Value = true;
                replaceNewlineProperty.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
            }
        }

        public static IDTSCustomProperty100 GetRowCountVariableProperty(IDTSComponentMetaData100 componentMetaData)
        {
            IDTSCustomProperty100 rowCountVariableProperty = null;

            foreach (IDTSCustomProperty100 customProperty in componentMetaData.CustomPropertyCollection)
            {
                if (customProperty.Name == PROPERTY_NAME_ROW_COUNT_VARIABLE)
                {
                    rowCountVariableProperty = customProperty;
                }
            }

            return rowCountVariableProperty;
        }

        private void AddRowCountVariableProperty()
        {
            if (HTMLTableDestination.GetRowCountVariableProperty(ComponentMetaData) == null)
            {
                IDTSCustomProperty100 rowCountVariableProperty = this.ComponentMetaData.CustomPropertyCollection.New();
                rowCountVariableProperty.Name = PROPERTY_NAME_ROW_COUNT_VARIABLE;
                rowCountVariableProperty.Value = "";
                rowCountVariableProperty.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
            }
        }

        public static IDTSCustomProperty100 GetColumnOrderProperty(IDTSComponentMetaData100 componentMetaData)
        {
            IDTSCustomProperty100 columnSetupProperty = null;

            foreach (IDTSCustomProperty100 customProperty in componentMetaData.CustomPropertyCollection)
            {
                if (customProperty.Name == PROPERTY_NAME_COLUMN_ORDER)
                {
                    columnSetupProperty = customProperty;
                }
            }

            return columnSetupProperty;
        }

        private void AddColumnOrderProperty()
        {
            if (HTMLTableDestination.GetColumnOrderProperty(ComponentMetaData) == null)
            {
                IDTSCustomProperty100 columnSetupProperty = this.ComponentMetaData.CustomPropertyCollection.New();
                columnSetupProperty.Name = PROPERTY_NAME_COLUMN_ORDER;
                columnSetupProperty.Value = new int[0];
                columnSetupProperty.ContainsID = true;
                columnSetupProperty.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
            }
        }

        private IDTSInput100 GetInput()
        {
            IDTSInput100 input = null;

            foreach (IDTSInput100 possibleInput in ComponentMetaData.InputCollection)
            {
                if (possibleInput.Name == INPUT_NAME)
                {
                    input = possibleInput;
                }
            }

            return input;
        }
        #endregion
    }
}